﻿#include "Mp4Decoder.h"
#include <memory>
#include <QDebug>

#pragma comment(lib,"libmp4v2.lib")

Mp4Decoder::Mp4Decoder()
{
	_mp4FileHandle = MP4_INVALID_FILE_HANDLE;
	_mp4Duration = 0;
	_audioInfo = nullptr;
	_audio_name = nullptr;
	_video_name = nullptr;
}

Mp4Decoder::~Mp4Decoder()
{
	close();
}

/** 打开解码器 */
bool Mp4Decoder::open(const std::string& filePathName)
{
	if (!filePathName.size())
	{
		return false;
	}

	_mp4FileHandle = MP4Read(filePathName.c_str());

	if (_mp4FileHandle == MP4_INVALID_FILE_HANDLE)
	{
		return false;
	}

	parse(_mp4FileHandle);

	return true;
}

/** 关闭解码器 */
void Mp4Decoder::close()
{
	if (_mp4FileHandle != MP4_INVALID_FILE_HANDLE)
	{
		MP4Close(_mp4FileHandle);
		_mp4FileHandle = MP4_INVALID_FILE_HANDLE;
	}

	if (_audioInfo)
	{
		MP4Free(_audioInfo);
		_audioInfo = nullptr;
	}

	_mp4Duration = 0;
	_audio_name = nullptr;
	_video_name = nullptr;

	videoComplete = false;
	audioComplete = false;

	videoSampleIndex = 1;
	audioSampleIndex = 1;
}

/** 读取一帧视频 */
bool Mp4Decoder::readVideo(Mp4DecoderData* data)
{
	// 读取一帧视频帧
	if (!videoComplete && !readOneFrame(_mp4FileHandle, _tidVideo, videoSampleIndex++, data))
	{
		videoComplete = true;
	}

	// 视频读取全部结束
	if (videoComplete)
	{
		return false;
	}

	return true;
}

/** 读取一帧音频 */
bool Mp4Decoder::readAudio(Mp4DecoderData* data)
{
	// 读取一帧音频帧
	if (!audioComplete && !readOneFrame(_mp4FileHandle, _tidAudio, audioSampleIndex++, data))
	{
		audioComplete = true;
	}

	// 音频读取全部结束
	if (audioComplete)
	{
		return false;
	}

	return false;
}

/** 解析MP4音视频数据帧 */
bool Mp4Decoder::parse(MP4FileHandle inFile)
{
	if (inFile == MP4_INVALID_FILE_HANDLE)
	{
		return false;
	}

	// 首先获取文件的每秒刻度数和总刻度数(不是毫秒数)
	uint32_t fileScale = MP4GetTimeScale(inFile);
	MP4Duration duration = MP4GetDuration(inFile);

	// 总毫秒数 = 总刻度数*1000/每秒刻度数 => 先乘法可以降低误差
	_mp4Duration = duration * 1000 / fileScale;

	// 获取需要的相关信息
	uint32_t trackCount = MP4GetNumberOfTracks(inFile);

	for (uint32_t i = 0; i < trackCount; i++)
	{
		MP4TrackId id = MP4FindTrackId(inFile, i);
		const char* type = MP4GetTrackType(inFile, id);

		if (MP4_IS_VIDEO_TRACK_TYPE(type))
		{
			// 视频已经有效，检测下一个
			if (_tidVideo > 0)
			{
				continue;
			}

			_tidVideo = id;

			// 获取视频信息
			char* videoInfo = MP4Info(inFile, _tidVideo);
			MP4Free(videoInfo);

			// 获取视频时间间隔
			_video_time_scale = MP4GetTrackTimeScale(inFile, _tidVideo);
			_video_name = MP4GetTrackMediaDataName(inFile, id);

			// 获取视频的PPS/SPS信息
			uint8_t** spsHeader = nullptr;
			uint8_t** ppsHeader = nullptr;
			uint32_t* spsSize = nullptr;
			uint32_t* ppsSize = nullptr;
			uint32_t ix = 0;

			bool result = MP4GetTrackH264SeqPictHeaders(inFile, id, &spsHeader, &spsSize, &ppsHeader, &ppsSize);

			// sps
			for (ix = 0; spsSize[ix] != 0; ++ix) 
			{
				// SPS指针和长度
				uint8_t* sps = spsHeader[ix];
				uint32_t size = spsSize[ix];

				// 只存储第一个SPS信息
				if (_stringSPS.size() <= 0 && size > 0)
				{
					_stringSPS.assign((char*)sps, size);
				}
				MP4Free(spsHeader[ix]);
			}

			MP4Free(spsHeader);
			MP4Free(spsSize);

			// pps
			for (ix = 0; ppsSize[ix] != 0; ++ix) 
			{
				// PPS指针和长度
				uint8_t* pps = ppsHeader[ix];
				uint32_t size = ppsSize[ix];

				// 只存储第一个PPS信息
				if (_stringPPS.size() <= 0 && size > 0)
				{
					_stringPPS.assign((char*)pps, size);
				}
				MP4Free(ppsHeader[ix]);
			}

			MP4Free(ppsHeader);
			MP4Free(ppsSize);
		}
		else if (MP4_IS_AUDIO_TRACK_TYPE(type)) 
		{
			// 音频已经有效，检测下一个
			if (_tidAudio > 0)
			{
				continue;
			}

			// 获取音频信息
			_tidAudio = id;

			_audioInfo = MP4Info(inFile, _tidAudio);

			// 获取音频的类型/名称/采样率/声道信息
			_audio_type = MP4GetTrackAudioMpeg4Type(inFile, _tidAudio);
			_audio_channel_num = MP4GetTrackAudioChannels(inFile, _tidAudio);
			_audio_name = MP4GetTrackMediaDataName(inFile, _tidAudio);

			// 音频采样率
			_audio_time_scale = MP4GetTrackTimeScale(inFile, _tidAudio);
			
			// 获取音频扩展信息
			uint8_t* aes = nullptr;
			uint32_t size = 0;
			bool haveEs = MP4GetTrackESConfiguration(inFile, _tidAudio, &aes, &size);

			// 存储音频扩展信息
			if (_stringAES.size() <= 0 && size > 0)
			{
				_stringAES.assign((char*)aes, size);
			}

			// 释放分配的缓存
			if (aes != nullptr)
			{
				MP4Free(aes);
				aes = nullptr;
			}
		}
	}

	// 如果音频和视频都没有，返回失败
	if (_tidVideo == MP4_INVALID_TRACK_ID && _tidAudio == MP4_INVALID_TRACK_ID)
	{
		return false;
	}

	return true;
}

/** 抽取一帧音频或视频数据帧 */
bool Mp4Decoder::readOneFrame(MP4FileHandle inFile, MP4TrackId tid, uint32_t sid, Mp4DecoderData* data)
{
	// 读取轨道的采样率和当前帧的时间戳
	data->timescale = MP4GetTrackTimeScale(inFile, tid);
	data->msectime = MP4GetSampleTime(inFile, tid, sid);

	// 读取一帧数据帧，失败，直接返回
	if (!MP4ReadSample(inFile, tid, sid, &data->sampleData, &data->sampleSize,
		&data->startTime, &data->duration, &data->offset, &data->isKeyFrame))
	{
		return false;
	}

	// 计算当前读取数据帧的时间戳
	// 计算发送时间 => PTS => 刻度时间转换成毫秒
	data->msectime *= UINT64_C(1000);
	data->msectime /= data->timescale;

	// 计算开始时间 => DTS => 刻度时间转换成毫秒
	data->startTime *= UINT64_C(1000);
	data->startTime /= data->timescale;

	// 计算偏差时间 => CTTS => 刻度时间转换成毫秒
	data->offset *= UINT64_C(1000);
	data->offset /= data->timescale;

	// 帧持续时间 => 刻度时间转换成毫秒
	data->duration *= UINT64_C(1000);
	data->duration /= data->timescale;

	return true;
}
